﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using ZeroBugBounce.Missive;

namespace WinFormsCommands
{
	public class CommandProcessor
	{
		Type RootViewType;
		Form RootView;
		ICommand StartupCommand;
		Assembly ViewAssembly;
		Thread UIThread;
		Dictionary<string, ViewPanelBase> ViewCache;

		internal Messenger Messenger;

		public CommandProcessor(Type rootViewType, Assembly viewAssembly, ICommand firstCommand)
		{
			RootViewType = rootViewType;
			ViewAssembly = viewAssembly;
			StartupCommand = firstCommand;
		}

		void PrimeViewCache(Assembly viewAssembly)
		{
			ViewCache = new Dictionary<string, ViewPanelBase>();
			ViewCache =
				viewAssembly
					.GetTypes().Where(t => typeof(ViewPanelBase).IsAssignableFrom(t))
					.ToDictionary(t => t.Name.ToLower(), t =>
					{
						var view = Activator.CreateInstance(t) as ViewPanelBase;
						view.Left = 11;
						view.Top = 129;
						view.Processor = this;

						view.Refresh();

						return view;
					});
		}

		internal ViewPanelBase FindView(CommandResult actionResult)
		{
			var key = actionResult.View.ToLower();
			key = key.EndsWith("view") ? key : key + "view";

			if (ViewCache.ContainsKey(key))
			{
				return ViewCache[key];
			}
			else
			{
				throw new InvalidOperationException("All views should be pre-cached");
			}
		}

		internal Action RemoveLastView;

		internal void InitializeMessaging()
		{
			Messenger = new Messenger();
			Messenger.Handle((ICommand command) =>
			{
				var result = ExecuteCommand(command);
				if (result.Continuation == null)
				{
					ProcessViewResult(result);
				}
				
			});
		}

		CommandResult ExecuteCommand(ICommand command)
		{	
			var result = command.Execute(new CommandContext(this));
			return result;
		}

		internal void ProcessViewResult(CommandResult viewResult)
		{
			var view = FindView(viewResult);
			Thread.Sleep(100);
			RootView.SyncWithUI(() =>
			{
				view.BindModel(viewResult.Model);
				if (viewResult.Target == null)
				{
					if (RemoveLastView != null)
					{
						RemoveLastView();
					}

					RootView.Controls.Add(view);
					RemoveLastView = () =>
					{
						RootView.Controls.Remove(view);
					};
				}
			});
		}

		public void Start()
		{
			InitializeMessaging();

			UIThread = new Thread(() =>
			{
				PrimeViewCache(ViewAssembly);
				RootView = Activator.CreateInstance(RootViewType) as Form;
				RootView.Load += (_, __) =>
				{
					Messenger.Send(StartupCommand);
				};

				Application.Run(RootView);
			});

			UIThread.SetApartmentState(ApartmentState.STA);
			UIThread.IsBackground = false;
			UIThread.Start();
		}
	}
}
